package models

import (
	"github.com/Unknwon/com"
	"github.com/astaxie/beego/orm"
	_ "github.com/mattn/go-sqlite3"
	"os"
	"path"
	"strconv"
	"strings"
	"time"
)

const (
	_DB_NAME       = "data/beeblog.db"
	_SQLITE_DRIVER = "sqlite3"
)

type Comment struct {
	Id       int64
	Tid      int64
	Nickname string    `orm:"index"`
	Content  string    `orm:"size(1000)"`
	Created  time.Time `orm:"index"`
}

type Category struct {
	Id              int64
	Title           string
	Created         time.Time `orm:"index"`
	Views           int64     `orm:"index"`
	TopicTime       time.Time `orm:"index"`
	TopicCount      int64
	TopicLastUserId int64
}

type Topic struct {
	Id              int64
	Uid             int64
	Title           string
	Category        string
	Labels          string
	Content         string `orm:"size(5000)"`
	Attachment      string
	Created         time.Time `orm:"index"`
	Updated         time.Time `orm:"index"`
	Views           int64     `orm:"index"`
	Author          string
	ReplyTime       time.Time `orm:"index"`
	ReplyCount      int64
	ReplyLastUserId int64
}

func DeleteReply(rid, tid string) error {
	ridNum, err := strconv.ParseInt(rid, 10, 64)
	if err != nil {
		return err
	}
	reply := &Comment{
		Id: ridNum,
	}
	o := orm.NewOrm()
	if _, err = o.Delete(reply); err != nil {
		return err
	}
	return UpdateTopic(tid, 0, -1)
}

func AddReply(tid, nickname, content string) error {
	tidNum, err := strconv.ParseInt(tid, 10, 64)
	if err != nil {
		return err
	}
	reply := &Comment{
		Tid:      tidNum,
		Nickname: nickname,
		Content:  content,
		Created:  time.Now(),
	}
	o := orm.NewOrm()
	if _, err = o.Insert(reply); err != nil {
		return err
	}
	return UpdateTopic(tid, 0, 1)
}

func UpdateTopic(tid string, viewsIncr, replyIncr int64) error {
	if topic, err := GetTopic(tid); err != nil {
		return err
	} else {
		topic.Views += viewsIncr
		topic.ReplyCount += replyIncr
		topic.Updated = time.Now()
		_, err := orm.NewOrm().Update(topic)
		return err
	}
}

func GetAllReplies(tid string) ([]*Comment, error) {
	tidNum, err := strconv.ParseInt(tid, 10, 64)
	if err != nil {
		return nil, err
	}
	reply := &Comment{
		Tid: tidNum,
	}
	o := orm.NewOrm()
	replies := make([]*Comment, 0)
	_, err = o.QueryTable(reply).Filter("tid", tidNum).OrderBy("-created").All(&replies)
	return replies, err
}

func DeleteTopic(tid string) error {
	id, err := strconv.ParseInt(tid, 10, 64)
	if err != nil {
		return err
	}
	orm := orm.NewOrm()
	var category string
	if topic, err := GetTopic(tid); err != nil {
		return err
	} else {
		category = topic.Category
	}
	if _, err = orm.Delete(&Topic{Id: id}); err != nil {
		return err
	}
	return updateCategory(category, -1)
}
func ModifyTopic(tid, title, category, labels, content, attachment string) error {
	labels = "#" + strings.Join(strings.Split(labels, " "), "$#") + "$"
	id, err := strconv.ParseInt(tid, 10, 64)
	if err != nil {
		return err
	}
	orm := orm.NewOrm()
	topic := &Topic{Id: id}
	err = orm.Read(topic)
	if err != nil {
		return err
	}
	topic.Title = title
	topic.Labels = labels
	topic.Content = content
	topic.Updated = time.Now()
	if topic.Category != category {
		oldCate := topic.Category
		topic.Category = category
		if len(oldCate) > 0 {
			updateCategory(oldCate, -1)
		}
		if len(topic.Category) > 0 {
			updateCategory(topic.Category, 1)
		}
	}
	if topic.Attachment != attachment {
		oldAttachment := topic.Attachment
		topic.Attachment = attachment
		if len(oldAttachment) > 0 {
			os.Remove(path.Join("attachment", tid, oldAttachment))
		}
	}
	_, err = orm.Update(topic)
	return err
}

func GetTopic(tid string) (*Topic, error) {
	id, err := strconv.ParseInt(tid, 10, 64)
	if err != nil {
		return nil, err
	}
	orm := orm.NewOrm()
	topic := new(Topic)
	err = orm.QueryTable(topic).Filter("id", id).One(topic)
	labels := topic.Labels
	if len(labels) > 0 {
		labels = labels[1:]
	}
	topic.Labels = strings.Replace(strings.Replace(labels, "$", "", -1), "#", " ", -1)
	return topic, err
}

func AddTopic(title, category, labels, content string) (int64, error) {
	labels = "#" + strings.Join(strings.Split(labels, " "), "$#") + "$"
	orm := orm.NewOrm()
	now := time.Now()
	topic := &Topic{Title: title, Category: category, Labels: labels, Created: now, Updated: now, ReplyTime: now, Content: content}
	var id int64
	var err error
	if id, err = orm.Insert(topic); err != nil {
	} else {
		err = updateCategory(category, 1)
	}
	return id, err
}

func updateCategory(title string, topicIncr int64) error {
	category := &Category{}
	o := orm.NewOrm()
	if err := o.QueryTable(category).Filter("title", title).One(category); err != nil {
		return err
	} else {
		category.TopicCount += topicIncr
		category.TopicTime = time.Now()
		_, err := o.Update(category)
		return err
	}
}

func GetAllTopics(cate, label string, isDesc bool) (c []*Topic, err error) {
	orm := orm.NewOrm()
	c = make([]*Topic, 0)
	qs := orm.QueryTable(&Topic{})
	if isDesc {
		if len(cate) > 0 {
			qs = qs.Filter("category", cate)
		}
		if len(label) > 0 {
			qs = qs.Filter("labels__contains", "#"+label+"$")
		}
		qs = qs.OrderBy("-created")
	}
	_, err = qs.All(&c)
	return
}

func AddCategory(name string) error {
	orm := orm.NewOrm()
	category := &Category{Title: name}
	err := orm.QueryTable(category).Filter("title", name).One(category)
	if err != nil {
		now := time.Now()
		category.Created = now
		category.TopicTime = now
		_, err = orm.Insert(category)
	}
	return err
}
func GetAllCategories() (c []*Category, err error) {
	orm := orm.NewOrm()
	c = make([]*Category, 0)
	_, err = orm.QueryTable(&Category{}).All(&c)
	return
}

func DelCategory(id string) error {
	orm := orm.NewOrm()
	cid, err := strconv.ParseInt(id, 10, 64)
	if err != nil {
		return err
	}
	_, err = orm.Delete(&Category{Id: cid})
	return err
}

func RegisterDB() {
	if !com.IsExist(_DB_NAME) {
		os.MkdirAll(path.Dir(_DB_NAME), os.ModePerm)
		os.Create(_DB_NAME)
	}
	orm.RegisterModel(new(Category), new(Topic), &Comment{})
	orm.RegisterDriver(_SQLITE_DRIVER, orm.DRSqlite)
	orm.RegisterDataBase("default", _SQLITE_DRIVER, _DB_NAME, 10)

}
